#include <s32file.h>
#include "MapUtils.h"
#include "MapTileServer.h"
#include "SingaporeBusDocument.h"
#include "PublicTransportMapContainerView.h"

void CMapTileServer::SetCallback(scalp::CFunction<void()>* aCallback)
	{
	iCallback = aCallback;
	}

void CMapTileServer::RunCallback()
	{
	iRunCallbackPending = EFalse;
	(*iCallback.Get())();
	}

void CMapTileServer::ConnectL()
	{
	CSingaporeBusDocument* doc = (CSingaporeBusDocument*)CEikonEnv::Static()->EikAppUi()->Document();
	doc->ConnectL();
	}

void CMapTileServer::LocalMsgQueueCallbackL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	scalp::CFunction<void()>* function;
	if ( iLocalMsgQueueActiveTarget->iStatus.Int() == KErrNone ) 
		{
		while(iLocalMsgQueue.Receive(function) == KErrNone)
			{
			(*function)();
			iWorkerThread->Execute( scalp::MakeFunctionL( scalp::Bind(&CMapTileServer::DeleteRemoteFunction, this, function) ) );
			}
		}
	iLocalMsgQueue.NotifyDataAvailable(iLocalMsgQueueActiveTarget->iStatus);
	iLocalMsgQueueActiveTarget->SetActive();
	}

void CMapTileServer::DownloadVisibleTilesL(TCoordinate aOrig, TInt aZoomLevel, TSize aSize)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iParamsMutex.Wait();
	iOrig = aOrig;
	iZoomLevel = aZoomLevel;
	iSize = aSize;
	iParamsMutex.Signal();
	if(!iDownloadVisibleTilesPending)
		{
		iDownloadVisibleTilesPending = ETrue;
		iWorkerThread->Execute( scalp::MakeFunctionL( scalp::Bind(&CMapTileServer::RemoteDownloadVisibleTilesL, this) ) );
		}
	}

void CMapTileServer::SetTileCacheSizeL(TInt aSize)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	TRequestStatus iStatus = KRequestPending;
	iWorkerThread->Execute( scalp::MakeFunctionL(scalp::Bind(&CMapTileServer::RemoteSetTileCacheSizeL, this, aSize, &iStatus)) );
	User::WaitForRequest(iStatus);
	}
void CMapTileServer::ClearTileCache()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	TRequestStatus iStatus = KRequestPending;
	iWorkerThread->Execute( scalp::MakeFunctionL(scalp::Bind(&CMapTileServer::RemoteClearTileCache, this, &iStatus)) );
	User::WaitForRequest(iStatus);
	}

CFbsBitmap* CMapTileServer::GetTileBitmap(TInt aTileX, TInt aTileY, TInt aZoomLevel) const
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	iTilesMutex.Wait();
	TInt i;
	for(i = 0 ; i < iTiles.Count() && !(iTiles[i]->iX == aTileX && iTiles[i]->iY == aTileY && iTiles[i]->iZoom == aZoomLevel) ; ++i);
	CFbsBitmap* ret = NULL;
	if(i != iTiles.Count())
		{
		if(iTiles[i]->iLocalBitmap == NULL)
			iTiles[i]->iLocalBitmap = new(ELeave)CFbsBitmap(); // no need to push in the cleanup stack thanks to imageDecoderInfo	
		if(iTiles[i]->iLocalBitmapHandle != iTiles[i]->iBitmapHandle)
			{
			iTiles[i]->iLocalBitmapHandle = iTiles[i]->iBitmapHandle;
			iTiles[i]->iLocalBitmap->Duplicate(iTiles[i]->iLocalBitmapHandle);
			}
		ret = (iTiles[i]->iStatus != CTileInfo::EDone) ? NULL : iTiles[i]->iLocalBitmap;
		}
	iTilesMutex.Signal();
	return ret;
	}

void CMapTileServer::ConstructL(CWorkerThread* aWorkerThread, TFileName aTileCachePath, TInt aCachedTilesCount)
	{
	iTileCachePath = aTileCachePath;
	iWorkerThread = aWorkerThread;
	iLocalThreadId = RThread().Id();
	iCachedTilesCount = aCachedTilesCount;

	iTilesSize = TSize(256, 256);
	User::LeaveIfError( iTilesMutex.CreateLocal() );
	User::LeaveIfError( iParamsMutex.CreateLocal() );
		
	iLocalMsgQueue.CreateLocal(128);
	iLocalMsgQueueActiveTarget = new scalp::CActiveTarget(
			scalp::MakeFunctionL(scalp::Bind(&RMsgQueueBase::CancelDataAvailable, iLocalMsgQueue))
			);
	iLocalMsgQueueActiveTarget->AppendRunL( scalp::MakeFunctionL(scalp::Bind(&CMapTileServer::LocalMsgQueueCallbackL, this)) );

	iLocalMsgQueue.NotifyDataAvailable(iLocalMsgQueueActiveTarget->iStatus);
	iLocalMsgQueueActiveTarget->SetActive();
	
	iWorkerThread->Execute( scalp::MakeFunctionL(scalp::Bind(&CMapTileServer::InitWorkerThread, this)) );
	}

CMapTileServer::~CMapTileServer()
	{
	
	iTilesMutex.Wait();
	for(TInt i = 0 ; i < iTiles.Count() ; ++i)
		delete iTiles[i]->iLocalBitmap; // must be deleted in local thread
	iTilesMutex.Signal();
	TRequestStatus iStatus = KRequestPending;
	iWorkerThread->Execute( scalp::MakeFunctionL(scalp::Bind(&CMapTileServer::UninitWorkerThread, this, &iStatus)) );
	User::WaitForRequest(iStatus);

	scalp::CFunction<void()>* function;
	while(iLocalMsgQueue.Receive(function) == KErrNone)
		delete function;
	delete iLocalMsgQueueActiveTarget;
	iLocalMsgQueue.Close();
	iTilesMutex.Close();
	iParamsMutex.Close();
	}

void CMapTileServer::SetConnectionInfoL(const RConnection& aConnection, const RSocketServ& aSocketServ)
	{
	TRequestStatus iStatus = KRequestPending;
	iWorkerThread->Execute( scalp::MakeFunctionL(scalp::Bind(&CMapTileServer::RemoteSetConnectionInfoL, this, &aConnection, &aSocketServ, &iStatus)) );
	User::WaitForRequest(iStatus);
	}
void CMapTileServer::CloseConnectionL()
	{
	TRequestStatus iStatus = KRequestPending;
	iWorkerThread->Execute( scalp::MakeFunctionL(scalp::Bind(&CMapTileServer::RemoteCloseConnectionL, this, &iStatus)) );
	User::WaitForRequest(iStatus);
	}




// ================================
// Remote thread
// ================================
const TInt kTileTimeout = 45*1000000;

void CMapTileServer::RemoteSetConnectionInfoL(const RConnection* aConnection, const RSocketServ* aSocketServ, TRequestStatus* aStatus)
	{
	iPublicTransportTileSession->SetConnectionInfoL(*aConnection, *aSocketServ);
	if(aStatus)
		{
		RThread thread;
		thread.Open(iLocalThreadId);
		thread.RequestComplete(aStatus, KErrNone);
		thread.Close();
		}
	}
void CMapTileServer::RemoteCloseConnectionL(TRequestStatus* aStatus)
	{
	iPublicTransportTileSession->CloseConnectionL();
	if(aStatus)
		{
		RThread thread;
		thread.Open(iLocalThreadId);
		thread.RequestComplete(aStatus, KErrNone);
		thread.Close();
		}
	}


void CMapTileServer::DeleteRemoteFunction(scalp::CFunction<void()>* aFunction)
	{
	delete aFunction;
	}

static void getTileFileName(TInt aTileX, TInt aTileY, TInt aZoom, TDes& aDes)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	_LIT(kFileNameFormat, "%d-%d-%d.png");
	aDes.Format(kFileNameFormat, aTileX, aTileY, aZoom);
	}

extern RFile sRemoteLogFile;

void CMapTileServer::InitWorkerThread()
	{
	iFsTileCache.Connect();
	iFsTileCache.SetSessionPath(iTileCachePath);
	iFsTileCache.MkDirAll(iTileCachePath);
	iTileTimeoutTimer.CreateLocal();
	iTileTimeoutTimerHelper = CActiveHelper<RTimer*>::NewL();
	iPublicTransportTileSession = CPublicTransportTileSession::NewL();
	LoadTileCacheL();
	RemoteSetTileCacheSizeL(iCachedTilesCount, NULL); // for cleanup if required
	}
void CMapTileServer::UninitWorkerThread(TRequestStatus* aStatus)
	{
	SaveTileCacheL();
	for(TInt i = 0 ; i < iTiles.Count() ; ++i)
		delete iTiles[i];
	iTiles.Close();
	delete iPublicTransportTileSession;
	delete iTileTimeoutTimerHelper;
	iTileTimeoutTimer.Close();
	for(TInt i = 0 ; i < iTileCache.Count() ; ++i)
		delete iTileCache[i];
	iTileCache.Close();
	iFsTileCache.Close();
	if(aStatus)
		{
		RThread thread;
		thread.Open(iLocalThreadId);
		thread.RequestComplete(aStatus, KErrNone);
		thread.Close();
		}
	}


void CMapTileServer::RemoteDownloadVisibleTilesL()
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	// bounding box of visible tiles:
	/*
	K = max(k from N) \  k*A < B
	<=>
	K = ((B - 1) - (B - 1)%A)/A;
	*/

	// order is important, we want to avoid:
/*
thread 2- get params
thread 1- push params
thread 1- check already run
thread 2- set already run
 */
	iDownloadVisibleTilesPending = EFalse;
	iParamsMutex.Wait();
	TCoordinate orig = iOrig;
	TInt zoomLevel = iZoomLevel;
	TSize size = iSize;
	iParamsMutex.Signal();
	
	
#if defined(MAPQUEST)
	int begtilex = (orig.Longitude() - (-180)) / (kTilesResolutions[zoomLevel] * iTilesSize.iWidth);
	int begdrawx = (-180 - orig.Longitude()) / kTilesResolutions[zoomLevel] + begtilex * iTilesSize.iWidth;
	int endtilex = begtilex + (size.iWidth - begdrawx - 1)/iTilesSize.iWidth;
	int begtiley = Lat2TileY(orig.Latitude(), 11 + zoomLevel);
	int endtiley = Lat2TileY(orig.Latitude() - size.iHeight * YScale(orig) * kTilesResolutions[zoomLevel], 11 + zoomLevel);	
	int begdrawy = (orig.Latitude() - TileY2Lat(begtiley, 11 + zoomLevel)) / (kTilesResolutions[zoomLevel] * YScale(orig));
#elif defined(ONEMAP)
	TENCoordinate origENCoord = CnvLL2EN(orig.Latitude(), orig.Longitude());
	int begtilex = (origENCoord.iEasting - kTileOrigE) / (kTilesResolutionsSVY21[zoomLevel] * iTilesSize.iWidth);
	int begtiley = (kTileOrigN - origENCoord.iNorthing) / (kTilesResolutionsSVY21[zoomLevel] * iTilesSize.iHeight);
	int begdrawx = (kTileOrigE - origENCoord.iEasting) / kTilesResolutionsSVY21[zoomLevel] + begtilex * iTilesSize.iWidth;
	int begdrawy =  - ((kTileOrigN - origENCoord.iNorthing) / kTilesResolutionsSVY21[zoomLevel] - begtiley * iTilesSize.iHeight);
	int endtilex = begtilex + (size.iWidth - begdrawx - 1)/iTilesSize.iWidth;
	int endtiley = begtiley  + (size.iHeight - begdrawy  - 1)/iTilesSize.iHeight;	
#endif
	
	
	iTilesMutex.Wait();
	for(TInt tilex = begtilex ; tilex <= endtilex ; ++tilex)
		for(TInt tiley = begtiley ; tiley <= endtiley ; ++tiley)
			{

			// search for the tile or a recyclable tile
			CTileInfo* recyclableTile = NULL;
			int i;
			for(i = 0 ; i < iTiles.Count() && !(iTiles[i]->iX == tilex && iTiles[i]->iY == tiley && iTiles[i]->iZoom == zoomLevel) ; ++i)
				{
				if(!recyclableTile && !(iTiles[i]->iZoom == zoomLevel && iTiles[i]->iX >= begtilex && iTiles[i]->iY >= begtiley && iTiles[i]->iX <= endtilex && iTiles[i]->iY <= endtiley))
					recyclableTile = iTiles[i];			
				}
	
			if(i != iTiles.Count() && iTiles[i]->iStatus != CTileInfo::EError)
				continue; // tile was found in either pending or done status. Nothing needs to be done here, just continue to next tile
			else
				{
				CTileInfo* tile;
				if(i != iTiles.Count() && iTiles[i]->iStatus == CTileInfo::EError)
					{
					tile = iTiles[i];
					}
				else if(recyclableTile)
					{
					tile = recyclableTile;
					if(tile->iTileRequest) tile->iTileRequest->Cancel();
					if(tile->iImageDecoderActiveHelper) tile->iImageDecoderActiveHelper->Cancel();
					if(tile->iFileReaderActiveHelper) tile->iFileReaderActiveHelper->Cancel();
					if(tile->iFileWriterActiveHelper) tile->iFileWriterActiveHelper->Cancel();
					tile->iFile.Close();
					tile->iWriteFile.Close();
					}
				else
					{
					tile = new (ELeave) CTileInfo;
					CleanupStack::PushL(tile);
					iTiles.AppendL(tile);
					CleanupStack::Pop(tile);
					}
				
				tile->iX = tilex;
				tile->iY = tiley;
				tile->iZoom = zoomLevel;
				tile->iStatus = CTileInfo::EReady;
				}
			}
		iTilesMutex.Signal();

	
		for(TInt i = 0 ; i < iTiles.Count() ; ++i)
			{
			CTileInfo* tile = iTiles[i];
			if(tile->iStatus == CTileInfo::EReady)
				{
				TFileName tileFileName;
				getTileFileName(tile->iX, tile->iY, tile->iZoom, tileFileName);
				TBool readFileFromFileSystem = EFalse;
				if(FileExistsInTileCacheL(tileFileName))
					{
					tile->iFile.Close();
					TInt ret = tile->iFile.Open(iFsTileCache, tileFileName, EFileRead | EFileShareReadersOrWriters);
					if(ret == KErrNone)
						{
						AddIntoTileCacheL(tileFileName);
						delete tile->iFileReaderActiveHelper;
						tile->iFileReaderActiveHelper = CActiveHelper<CTileInfo*>::NewL(CActive::EPriorityHigh);
						TInt size;
						tile->iFile.Size(size);
						tile->iFileBuf.Zero();
						if(size > tile->iFileBuf.MaxLength())
							tile->iFileBuf.ReAllocL(size);
						tile->iFile.Read(tile->iFileBuf, tile->iFileReaderActiveHelper->iStatus);
						tile->iFileReaderActiveHelper->SetActive(this, tile);
						readFileFromFileSystem = ETrue;
						}
					}
				if(!readFileFromFileSystem)
					{
					if(!tile->iTileRequest)
						tile->iTileRequest = CPublicTransportTileRequest::NewL(iPublicTransportTileSession);
					tile->iTileRequest->SubmitL(zoomLevel, tile->iX, tile->iY, this, tile);
					iTileTimeoutTimerHelper->Cancel();
					iTileTimeoutTimer.After(iTileTimeoutTimerHelper->iStatus, kTileTimeout);
					iTileTimeoutTimerHelper->SetActive(this, &iTileTimeoutTimer);					
					}
				tile->iStatus = CTileInfo::EPending;
				tile->iImageDecoderStarted = EFalse;
				}
			}
	
	}


TInt sLastLine;

void CMapTileServer::MPublicTransportTileCallbackRunL(CPublicTransportTileRequest* aTileRequest, TInt aZoom, TInt aTileX, TInt aTileY, TInt aStatus, TInt aLastError, TInt aStatusCode, const TDesC8& aData, void* aUserData)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	iTileTimeoutTimerHelper->Cancel();
	iTileTimeoutTimer.After(iTileTimeoutTimerHelper->iStatus, kTileTimeout);
	iTileTimeoutTimerHelper->SetActive(this, &iTileTimeoutTimer);		

	CTileInfo* tile = static_cast<CTileInfo*>(aUserData);
	
	iStatusCode = aStatusCode;
	if(aStatus != THTTPEvent::ESucceeded)
		User::Leave(aLastError < 0 ? aLastError : KErrAbort);

	delete tile->iImageDecoder;
	tile->iImageDecoder = NULL;
#ifdef __WINS__
	tile->iImageDecoder = CImageDecoder::DataNewL( iFsTileCache, aData );
#else
	tile->iImageDecoder = CImageDecoder::DataNewL( iFsTileCache, aData, CImageDecoder::EOptionAlwaysThread );
#endif
	{
	TBuf<KMaxFileName> tileFileName;
	getTileFileName(aTileX, aTileY, aZoom, tileFileName);
		{
		delete tile->iFileWriterActiveHelper;
		TInt ret = tile->iWriteFile.Replace(iFsTileCache, tileFileName, EFileWrite);
		if(ret == KErrNone)
			{
			tile->iFileWriterActiveHelper = CActiveHelper<CTileInfo*>::NewL(CActive::EPriorityHigh);
			tile->iWriteFile.Write(aData, tile->iFileWriterActiveHelper->iStatus);
			tile->iFileWriterActiveHelper->SetActive(this, tile);
			}
		}
	}
	if(!tile->iBitmap)
		{
		tile->iBitmap = new(ELeave)CFbsBitmap(); // no need to push in the cleanup stack thanks to imageDecoderInfo	
		User::LeaveIfError( tile->iBitmap->Create(tile->iImageDecoder->FrameInfo(0).iOverallSizeInPixels,tile->iImageDecoder->FrameInfo(0).iFrameDisplayMode) );		
		}
	if(!tile->iImageDecoderActiveHelper)
		tile->iImageDecoderActiveHelper = CActiveHelper<CTileInfo*>::NewL(CActive::EPriorityHigh);
	tile->iImageDecoderActiveHelper->Cancel();
	tile->iImageDecoder->Convert(&tile->iImageDecoderActiveHelper->iStatus, *tile->iBitmap);
	tile->iImageDecoderActiveHelper->SetActive(this, tile);
	tile->iImageDecoderStarted = ETrue;
	
	}
	
TInt CMapTileServer::MPublicTransportTileCallbackRunError(CPublicTransportTileRequest* aTileRequest, TInt aZoom, TInt aTileX, TInt aTileY, TInt aError, const TDesC8& aData, void* aUserData)
	{
	CTileInfo* tile = static_cast<CTileInfo*>(aUserData);
	tile->iStatus = CTileInfo::EError;
	if(aError == KErrNotReady || aError == KErrConnectionTerminated) // not connected or connection lost
		iLocalMsgQueue.SendBlocking( scalp::MakeFunctionL( scalp::Bind(&CMapTileServer::ConnectL, this) ) );
	else if(iStatusCode/100 == 5 || aError == KErrNotFound) // Server Error 5xx
		RemoteDownloadVisibleTilesL();
	iLocalMsgQueue.SendBlocking( scalp::MakeFunctionL( scalp::Bind(&CMapTileServer::RunCallback, this) ) );
	return KErrNone;
	}


void CMapTileServer::MActiveHelperCallbackRunL(CActiveHelper<RTimer*>* aActiveHelper, RTimer* aTimer)
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(aActiveHelper->iStatus == KErrNone)
	{
		iTilesMutex.Wait();
		for(TInt i = 0 ; i < iTiles.Count() ; ++i)
		{
		if(iTiles[i]->iStatus == CTileInfo::EPending)
			{
			CTileInfo* tile = iTiles[i];
			tile->iStatus = CTileInfo::EError;
			if(tile->iTileRequest) tile->iTileRequest->Cancel();
			if(tile->iImageDecoderActiveHelper) tile->iImageDecoderActiveHelper->Cancel();
			if(tile->iFileReaderActiveHelper) tile->iFileReaderActiveHelper->Cancel();
			if(tile->iFileWriterActiveHelper) tile->iFileWriterActiveHelper->Cancel();
			tile->iFile.Close();
			}
		}
		iTilesMutex.Signal();
		RemoteDownloadVisibleTilesL();
	}
}

TInt CMapTileServer::MActiveHelperCallbackRunError(CActiveHelper<RTimer*>* aActiveHelper, RTimer* aTimer, TInt aError)
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
}

void CMapTileServer::MActiveHelperCallbackDoCancel(CActiveHelper<RTimer*>* aActiveHelper, RTimer* aTimer)
{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	aTimer->Cancel();
}



void CMapTileServer::MActiveHelperCallbackRunL(CActiveHelper<CTileInfo*>* aActiveHelper, CTileInfo* aTile)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);

	if(aActiveHelper == aTile->iImageDecoderActiveHelper)
		{
		TInt ret = aActiveHelper->iStatus.Int();
		
		
		if(ret == KErrNone)
			{
			iTilesMutex.Wait();
			aTile->iStatus = CTileInfo::EDone;
			aTile->iBitmapHandle = aTile->iBitmap->Handle();
			iTilesMutex.Signal();
			if(!iRunCallbackPending)
				{
				iRunCallbackPending = ETrue;
				iLocalMsgQueue.SendBlocking( scalp::MakeFunctionL( scalp::Bind(&CMapTileServer::RunCallback, this) ) );
				}
			
/*			iParamsMutex.Wait();
			TCoordinate orig = iOrig;
			TInt zoomLevel = iZoomLevel;
			TSize size = iSize;
			iParamsMutex.Signal();
#if defined(MAPQUEST)
		int begtilex = (orig.Longitude() - (-180)) / (kTilesResolutions[zoomLevel] * iTilesSize.iWidth);
		int begdrawx = (-180 - orig.Longitude()) / kTilesResolutions[zoomLevel] + begtilex * iTilesSize.iWidth;
		int endtilex = begtilex + (Rect().Width() - begdrawx - 1)/iTilesSize.iWidth;
		int begtiley = Lat2TileY(orig.Latitude(), 11 + zoomLevel);
		int endtiley = Lat2TileY(orig.Latitude() - Rect().Height() * YScale() * kTilesResolutions[zoomLevel], 11 + zoomLevel);	
		int begdrawy = (orig.Latitude() - TileY2Lat(begtiley, 11 + zoomLevel)) / (kTilesResolutions[zoomLevel] * YScale());
#elif defined(ONEMAP)
		TENCoordinate origENCoord = CnvLL2EN(orig.Latitude(), orig.Longitude());
		int begtilex = (origENCoord.iEasting - kTileOrigE) / (kTilesResolutionsSVY21[zoomLevel] * iTilesSize.iWidth);
		int begtiley = (kTileOrigN - origENCoord.iNorthing) / (kTilesResolutionsSVY21[zoomLevel] * iTilesSize.iHeight);
		int begdrawx = (kTileOrigE - origENCoord.iEasting) / kTilesResolutionsSVY21[zoomLevel] + begtilex * iTilesSize.iWidth;
		int begdrawy =  - ((kTileOrigN - origENCoord.iNorthing) / kTilesResolutionsSVY21[zoomLevel] - begtiley * iTilesSize.iHeight);
		int endtilex = begtilex + (size.iWidth - begdrawx - 1)/iTilesSize.iWidth;
		int endtiley = begtiley  + (size.iHeight - begdrawy  - 1)/iTilesSize.iHeight;
#endif
		
		if(aTile->iX >= begtilex && aTile->iY >= begtiley && aTile->iX <= endtilex && aTile->iY <= endtiley) // tile is visible
			{
			if(!iRunCallbackPending)
				{
				iRunCallbackPending = ETrue;
				iLocalMsgQueue.SendBlocking( scalp::MakeFunctionL( scalp::Bind(&CMapTileServer::RunCallback, this) ) );
				}
			}*/
		
			}
		else
			aTile->iStatus = CTileInfo::EError;
		
		}
	else if(aActiveHelper == aTile->iFileReaderActiveHelper)
		{
		TInt ret = aActiveHelper->iStatus.Int();
		aTile->iFile.Close();
		if(ret == KErrNone)
			{
			TInt size = aTile->iFileBuf.Length();
			delete aTile->iImageDecoder;
			aTile->iImageDecoder = NULL;
			aTile->iImageDecoder = CImageDecoder::DataNewL( iFsTileCache, aTile->iFileBuf, CImageDecoder::EOptionAlwaysThread );
			if(!aTile->iBitmap)
				{
				aTile->iBitmap = new(ELeave)CFbsBitmap(); // no need to push in the cleanup stack thanks to imageDecoderInfo	
				User::LeaveIfError( aTile->iBitmap->Create(aTile->iImageDecoder->FrameInfo(0).iOverallSizeInPixels,aTile->iImageDecoder->FrameInfo(0).iFrameDisplayMode) );		
				}
			if(!aTile->iImageDecoderActiveHelper)
				aTile->iImageDecoderActiveHelper = CActiveHelper<CTileInfo*>::NewL(CActive::EPriorityHigh);
			aTile->iImageDecoderActiveHelper->Cancel();
			aTile->iImageDecoder->Convert(&aTile->iImageDecoderActiveHelper->iStatus, *aTile->iBitmap);
			aTile->iImageDecoderActiveHelper->SetActive(this, aTile);
			aTile->iImageDecoderStarted = ETrue;
			}
		}
	else if(aActiveHelper == aTile->iFileWriterActiveHelper)
		{
		TFileName tileFileName;
		getTileFileName(aTile->iX, aTile->iY, aTile->iZoom, tileFileName);
		if(aTile->iFileWriterActiveHelper->iStatus == KErrNone)
			AddIntoTileCacheL(tileFileName);
		else
			iFsTileCache.Delete(tileFileName);
		aTile->iWriteFile.Close();
		}
	
	}
TInt CMapTileServer::MActiveHelperCallbackRunError(CActiveHelper<CTileInfo*>* aActiveHelper, CTileInfo* aTile, TInt aError)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);

	}

void CMapTileServer::MActiveHelperCallbackDoCancel(CActiveHelper<CTileInfo*>* aActiveHelper, CTileInfo* aTile)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
if(aActiveHelper == aTile->iImageDecoderActiveHelper)
	{
	aTile->iImageDecoder->Cancel();
	}
else if(aActiveHelper == aTile->iFileReaderActiveHelper)
	{
	aTile->iFile.ReadCancel(aTile->iFileReaderActiveHelper->iStatus);
	}
else if(aActiveHelper == aTile->iFileWriterActiveHelper)
	{
	// there is no way to cancel a write operation
	}
	}


void CMapTileServer::MActiveHelperCallbackRunL(CActiveHelper<CImageDecoderEx*>* aActiveHelper, CImageDecoderEx* aImageDecoder)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	iLocalMsgQueue.SendBlocking( scalp::MakeFunctionL( scalp::Bind(&CMapTileServer::RunCallback, this) ) );
	}
TInt CMapTileServer::MActiveHelperCallbackRunError(CActiveHelper<CImageDecoderEx*>* aActiveHelper, CImageDecoderEx* aImageDecoder, TInt aError)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	
	}
void CMapTileServer::MActiveHelperCallbackDoCancel(CActiveHelper<CImageDecoderEx*>* aActiveHelper, CImageDecoderEx* aImageDecoder)
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	aImageDecoder->Cancel();	
	}


void CMapTileServer::RemoteClearTileCache(TRequestStatus* aStatus)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	CFileMan* fileMan = CFileMan::NewL(iFsTileCache);
	CleanupStack::PushL(fileMan);
	TBuf<KMaxFileName> cachePattern;
	iFsTileCache.SessionPath(cachePattern);
	cachePattern.Append(_L("*"));
	for( ; fileMan->Delete(cachePattern) == KErrInUse ; User::After(1000000) );
	for(TInt i = 0 ; i < iTileCache.Count() ; ++i)
		delete iTileCache[i];
	iTileCache.Reset();
	CleanupStack::PopAndDestroy();	
	if(aStatus)
		{
		RThread thread;
		thread.Open(iLocalThreadId);
		thread.RequestComplete(aStatus, KErrNone);
		thread.Close();
		}
	}

TInt CMapTileServer::LoadTileCacheL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	RFileReadStream reader;
	reader.PushL();   // call CleanupReleasePushL()
	TInt err = reader.Open(iFsTileCache, _L("TileCache.ini"), EFileRead);
	if(err == KErrNone)
		{
		TUint version = reader.ReadUint32L(); // version number
		switch(version)
			{
			case 1:
				{
				TUint count = reader.ReadUint32L();
				for(TUint i = 0 ; i < count ; ++i)
					{
					TBuf<KMaxFileName> iFileName;
					reader >> iFileName;
					HBufC* hFileName = iFileName.AllocLC();
					iTileCache.AppendL(hFileName);
					CleanupStack::Pop(hFileName);
					}
				}
			}
		}
	CleanupStack::PopAndDestroy(&reader); // release resources 
	return err;	
	}

TInt CMapTileServer::SaveTileCacheL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
		if(!iFsTileCache.Handle()) // iFsTileCache not initialized!
			return KErrNotReady;
		RFileWriteStream writer;
		writer.PushL();   // call CleanupReleasePushL()
		TInt err = writer.Replace(iFsTileCache, _L("TileCache.ini"), EFileWrite);
		if(err == KErrNone)
			{
			writer.WriteUint32L(1); // version number
			writer.WriteUint32L(iTileCache.Count());
			for(TUint i = 0 ; i < iTileCache.Count() ; ++i)
				{
				writer << *iTileCache[i];
				}
			writer.CommitL(); // all buffered data is written out
			}
		CleanupStack::PopAndDestroy(&writer); // release resources 
		return err;
	}


TBool CMapTileServer::FileExistsInTileCacheL(const TDesC& aFileName)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	TInt i;
	for(i = 0 ; i < iTileCache.Count() && *iTileCache[i] != aFileName ; ++i);
	return (i != iTileCache.Count());
	}

void CMapTileServer::AddIntoTileCacheL(const TDesC& aFileName)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	TInt i;
	for(i = 0 ; i < iTileCache.Count() && *iTileCache[i] != aFileName ; ++i);
	if(i == iTileCache.Count())
		{
		// the filename does not exist in the cache
		if( iTileCache.Count() < iCachedTilesCount)
			{
			// cache is not full
			HBufC* hFileName = aFileName.AllocLC();
			iTileCache.InsertL(hFileName, 0);
			CleanupStack::Pop(hFileName);
			}
		else
			{
			// cache is full
			if(iFsTileCache.Delete( *iTileCache[iTileCache.Count()-1] ) == KErrNone)
				{
				delete iTileCache[iTileCache.Count()-1];
				iTileCache[iTileCache.Count()-1] = NULL;
				
				for(TInt j = iTileCache.Count() - 1 ; j > 0 ; --j)
					iTileCache[j] = iTileCache[j-1];
								
				HBufC* hFileName = aFileName.AllocLC();
				iTileCache[0] = hFileName;
				CleanupStack::Pop(hFileName);
				}
			else
				{
				HBufC* hFileName = aFileName.AllocLC();
				iTileCache.Insert(hFileName, 0);
				CleanupStack::Pop(hFileName);				
				}
			}
		}
	else
		{
		// the filname already exists in the cache
		HBufC* hSavFileName = iTileCache[i];

		for(TInt j = i ; j > 0 ; --j)
			iTileCache[j] = iTileCache[j-1];
			
		iTileCache[0] = hSavFileName;
		}
	}

void CMapTileServer::RemoteSetTileCacheSizeL(TInt aSize, TRequestStatus* aStatus)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iCachedTilesCount = aSize;
	for(TInt i = iTileCache.Count() - 1 ; i >= iCachedTilesCount ; --i)
		{
		TInt ret = iFsTileCache.Delete( *iTileCache[i] );
		if(ret == KErrNone || ret == KErrNotFound)
			{
			delete iTileCache[i];
			iTileCache.Remove(i);
			}
		}
	if(aStatus)
		{
		RThread thread;
		thread.Open(iLocalThreadId);
		thread.RequestComplete(aStatus, KErrNone);
		thread.Close();
		}
	}





